package thread.pool.core.util;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.util.AbstractQueue;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;

public class ResizableCapacityLinkedBlockIngQueue<E> extends AbstractQueue<E> implements BlockingDeque<E> {
  private static final long serialVersionUID = -387911632671998426L;
  
  transient Node<E> first;
  
  transient Node<E> last;
  
  private transient int count;
  
  private int capacity;
  
  static final class Node<E> {
    E item;
    
    Node<E> prev;
    
    Node<E> next;
    
    Node(E x) {
      this.item = x;
    }
  }
  
  final ReentrantLock lock = new ReentrantLock();
  
  private final Condition notEmpty = this.lock.newCondition();
  
  private final Condition notFull = this.lock.newCondition();
  
  public int getCapacity() {
    return this.capacity;
  }

  /**
   * 就是这里 改变队列大小
   * @param capacity 容量
   */
  public void setCapacity(int capacity) {
    this.capacity = capacity;
  }
  
  public ResizableCapacityLinkedBlockIngQueue() {
    this(2147483647);
  }
  
  public ResizableCapacityLinkedBlockIngQueue(int capacity) {
    if (capacity <= 0)
      throw new IllegalArgumentException(); 
    this.capacity = capacity;
  }
  
  public ResizableCapacityLinkedBlockIngQueue(Collection<? extends E> c) {
    this(2147483647);
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      for (E e : c) {
        if (e == null)
          throw new NullPointerException(); 
        if (!linkLast(new Node<>(e)))
          throw new IllegalStateException("Deque full"); 
      } 
    } finally {
      lock.unlock();
    } 
  }
  
  private boolean linkFirst(Node<E> node) {
    if (this.count >= this.capacity)
      return false; 
    Node<E> f = this.first;
    node.next = f;
    this.first = node;
    if (this.last == null) {
      this.last = node;
    } else {
      f.prev = node;
    } 
    this.count++;
    this.notEmpty.signal();
    return true;
  }
  
  private boolean linkLast(Node<E> node) {
    if (this.count >= this.capacity)
      return false; 
    Node<E> l = this.last;
    node.prev = l;
    this.last = node;
    if (this.first == null) {
      this.first = node;
    } else {
      l.next = node;
    } 
    this.count++;
    this.notEmpty.signal();
    return true;
  }
  
  private E unlinkFirst() {
    Node<E> f = this.first;
    if (f == null)
      return null; 
    Node<E> n = f.next;
    E item = f.item;
    f.item = null;
    f.next = f;
    this.first = n;
    if (n == null) {
      this.last = null;
    } else {
      n.prev = null;
    } 
    this.count--;
    this.notFull.signal();
    return item;
  }
  
  private E unlinkLast() {
    Node<E> l = this.last;
    if (l == null)
      return null; 
    Node<E> p = l.prev;
    E item = l.item;
    l.item = null;
    l.prev = l;
    this.last = p;
    if (p == null) {
      this.first = null;
    } else {
      p.next = null;
    } 
    this.count--;
    this.notFull.signal();
    return item;
  }
  
  void unlink(Node<E> x) {
    Node<E> p = x.prev;
    Node<E> n = x.next;
    if (p == null) {
      unlinkFirst();
    } else if (n == null) {
      unlinkLast();
    } else {
      p.next = n;
      n.prev = p;
      x.item = null;
      this.count--;
      this.notFull.signal();
    } 
  }
  
  public void addFirst(E e) {
    if (!offerFirst(e))
      throw new IllegalStateException("Deque full"); 
  }
  
  public void addLast(E e) {
    if (!offerLast(e))
      throw new IllegalStateException("Deque full"); 
  }
  
  public boolean offerFirst(E e) {
    if (e == null)
      throw new NullPointerException(); 
    Node<E> node = new Node<>(e);
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      return linkFirst(node);
    } finally {
      lock.unlock();
    } 
  }
  
  public boolean offerLast(E e) {
    if (e == null)
      throw new NullPointerException(); 
    Node<E> node = new Node<>(e);
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      return linkLast(node);
    } finally {
      lock.unlock();
    } 
  }
  
  public void putFirst(E e) throws InterruptedException {
    if (e == null)
      throw new NullPointerException(); 
    Node<E> node = new Node<>(e);
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      while (!linkFirst(node))
        this.notFull.await(); 
    } finally {
      lock.unlock();
    } 
  }
  
  public void putLast(E e) throws InterruptedException {
    if (e == null)
      throw new NullPointerException(); 
    Node<E> node = new Node<>(e);
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      while (!linkLast(node))
        this.notFull.await(); 
    } finally {
      lock.unlock();
    } 
  }
  
  public boolean offerFirst(E e, long timeout, TimeUnit unit) throws InterruptedException {
    if (e == null)
      throw new NullPointerException(); 
    Node<E> node = new Node<>(e);
    long nanos = unit.toNanos(timeout);
    ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
      while (!linkFirst(node)) {
        if (nanos <= 0L)
          return false; 
        nanos = this.notFull.awaitNanos(nanos);
      } 
      return true;
    } finally {
      lock.unlock();
    } 
  }
  
  public boolean offerLast(E e, long timeout, TimeUnit unit) throws InterruptedException {
    if (e == null)
      throw new NullPointerException(); 
    Node<E> node = new Node<>(e);
    long nanos = unit.toNanos(timeout);
    ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
      while (!linkLast(node)) {
        if (nanos <= 0L)
          return false; 
        nanos = this.notFull.awaitNanos(nanos);
      } 
      return true;
    } finally {
      lock.unlock();
    } 
  }
  
  public E removeFirst() {
    E x = pollFirst();
    if (x == null)
      throw new NoSuchElementException(); 
    return x;
  }
  
  public E removeLast() {
    E x = pollLast();
    if (x == null)
      throw new NoSuchElementException(); 
    return x;
  }
  
  public E pollFirst() {
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      return unlinkFirst();
    } finally {
      lock.unlock();
    } 
  }
  
  public E pollLast() {
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      return unlinkLast();
    } finally {
      lock.unlock();
    } 
  }
  
  public E takeFirst() throws InterruptedException {
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      E x;
      while ((x = unlinkFirst()) == null)
        this.notEmpty.await(); 
      return x;
    } finally {
      lock.unlock();
    } 
  }
  
  public E takeLast() throws InterruptedException {
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      E x;
      while ((x = unlinkLast()) == null)
        this.notEmpty.await(); 
      return x;
    } finally {
      lock.unlock();
    } 
  }
  
  public E pollFirst(long timeout, TimeUnit unit) throws InterruptedException {
    long nanos = unit.toNanos(timeout);
    ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
      E x;
      while ((x = unlinkFirst()) == null) {
        if (nanos <= 0L)
          return null; 
        nanos = this.notEmpty.awaitNanos(nanos);
      } 
      return x;
    } finally {
      lock.unlock();
    } 
  }
  
  public E pollLast(long timeout, TimeUnit unit) throws InterruptedException {
    long nanos = unit.toNanos(timeout);
    ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
      E x;
      while ((x = unlinkLast()) == null) {
        if (nanos <= 0L)
          return null; 
        nanos = this.notEmpty.awaitNanos(nanos);
      } 
      return x;
    } finally {
      lock.unlock();
    } 
  }
  
  public E getFirst() {
    E x = peekFirst();
    if (x == null)
      throw new NoSuchElementException(); 
    return x;
  }
  
  public E getLast() {
    E x = peekLast();
    if (x == null)
      throw new NoSuchElementException(); 
    return x;
  }
  
  public E peekFirst() {
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      return (this.first == null) ? null : this.first.item;
    } finally {
      lock.unlock();
    } 
  }
  
  public E peekLast() {
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      return (this.last == null) ? null : this.last.item;
    } finally {
      lock.unlock();
    } 
  }
  
  public boolean removeFirstOccurrence(Object o) {
    if (o == null)
      return false; 
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      for (Node<E> p = this.first; p != null; p = p.next) {
        if (o.equals(p.item)) {
          unlink(p);
          return true;
        } 
      } 
      return false;
    } finally {
      lock.unlock();
    } 
  }
  
  public boolean removeLastOccurrence(Object o) {
    if (o == null)
      return false; 
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      for (Node<E> p = this.last; p != null; p = p.prev) {
        if (o.equals(p.item)) {
          unlink(p);
          return true;
        } 
      } 
      return false;
    } finally {
      lock.unlock();
    } 
  }
  
  public boolean add(E e) {
    addLast(e);
    return true;
  }
  
  public boolean offer(E e) {
    return offerLast(e);
  }
  
  public void put(E e) throws InterruptedException {
    putLast(e);
  }
  
  public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
    return offerLast(e, timeout, unit);
  }
  
  public E remove() {
    return removeFirst();
  }
  
  public E poll() {
    return pollFirst();
  }
  
  public E take() throws InterruptedException {
    return takeFirst();
  }
  
  public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    return pollFirst(timeout, unit);
  }
  
  public E element() {
    return getFirst();
  }
  
  public E peek() {
    return peekFirst();
  }
  
  public int remainingCapacity() {
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      return this.capacity - this.count;
    } finally {
      lock.unlock();
    } 
  }
  
  public int drainTo(Collection<? super E> c) {
    return drainTo(c, 2147483647);
  }
  
  public int drainTo(Collection<? super E> c, int maxElements) {
    if (c == null)
      throw new NullPointerException(); 
    if (c == this)
      throw new IllegalArgumentException(); 
    if (maxElements <= 0)
      return 0; 
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      int n = Math.min(maxElements, this.count);
      int i;
      for (i = 0; i < n; i++) {
        c.add(this.first.item);
        unlinkFirst();
      } 
      i = n;
      return i;
    } finally {
      lock.unlock();
    } 
  }
  
  public void push(E e) {
    addFirst(e);
  }
  
  public E pop() {
    return removeFirst();
  }
  
  public boolean remove(Object o) {
    return removeFirstOccurrence(o);
  }
  
  public int size() {
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      return this.count;
    } finally {
      lock.unlock();
    } 
  }
  
  public boolean contains(Object o) {
    if (o == null)
      return false; 
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      for (Node<E> p = this.first; p != null; p = p.next) {
        if (o.equals(p.item))
          return true; 
      } 
      return false;
    } finally {
      lock.unlock();
    } 
  }
  
  public Object[] toArray() {
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      Object[] a = new Object[this.count];
      int k = 0;
      for (Node<E> p = this.first; p != null; p = p.next)
        a[k++] = p.item; 
      return a;
    } finally {
      lock.unlock();
    } 
  }
  
  public <T> T[] toArray(T[] a) {
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      if (a.length < this.count)
        a = (T[])Array.newInstance(a.getClass().getComponentType(), this.count); 
      int k = 0;
      for (Node<E> p = this.first; p != null; p = p.next)
        a[k++] = (T)p.item; 
      if (a.length > k)
        a[k] = null; 
      return a;
    } finally {
      lock.unlock();
    } 
  }
  
  public String toString() {
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      Node<E> p = this.first;
      if (p == null)
        return "[]"; 
      StringBuilder sb = new StringBuilder();
      sb.append('[');
      while (true) {
        E e = p.item;
        sb.append((e == this) ? "(this Collection)" : e);
        p = p.next;
        if (p == null)
          return sb.append(']').toString(); 
        sb.append(',').append(' ');
      } 
    } finally {
      lock.unlock();
    } 
  }
  
  public void clear() {
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      for (Node<E> f = this.first; f != null; ) {
        f.item = null;
        Node<E> n = f.next;
        f.prev = null;
        f.next = null;
        f = n;
      } 
      this.first = this.last = null;
      this.count = 0;
      this.notFull.signalAll();
    } finally {
      lock.unlock();
    } 
  }
  
  public Iterator<E> iterator() {
    return new Itr();
  }
  
  public Iterator<E> descendingIterator() {
    return new DescendingItr();
  }
  
  private abstract class AbstractItr implements Iterator<E> {
    Node<E> next;
    
    E nextItem;
    
    private Node<E> lastRet;
    
    abstract Node<E> firstNode();
    
    abstract Node<E> nextNode(Node<E> param1Node);
    
    AbstractItr() {
      ReentrantLock lock = ResizableCapacityLinkedBlockIngQueue.this.lock;
      lock.lock();
      try {
        this.next = firstNode();
        this.nextItem = (this.next == null) ? null : this.next.item;
      } finally {
        lock.unlock();
      } 
    }
    
    private Node<E> succ(Node<E> n) {
      while (true) {
        Node<E> s = nextNode(n);
        if (s == null)
          return null; 
        if (s.item != null)
          return s; 
        if (s == n)
          return firstNode(); 
        n = s;
      } 
    }
    
    void advance() {
      ReentrantLock lock = ResizableCapacityLinkedBlockIngQueue.this.lock;
      lock.lock();
      try {
        this.next = succ(this.next);
        this.nextItem = (this.next == null) ? null : this.next.item;
      } finally {
        lock.unlock();
      } 
    }
    
    public boolean hasNext() {
      return (this.next != null);
    }
    
    public E next() {
      if (this.next == null)
        throw new NoSuchElementException(); 
      this.lastRet = this.next;
      E x = this.nextItem;
      advance();
      return x;
    }
    
    public void remove() {
      Node<E> n = this.lastRet;
      if (n == null)
        throw new IllegalStateException(); 
      this.lastRet = null;
      ReentrantLock lock = ResizableCapacityLinkedBlockIngQueue.this.lock;
      lock.lock();
      try {
        if (n.item != null)
          ResizableCapacityLinkedBlockIngQueue.this.unlink(n); 
      } finally {
        lock.unlock();
      } 
    }
  }
  
  private class Itr extends AbstractItr {
    private Itr() {}
    
    Node<E> firstNode() {
      return ResizableCapacityLinkedBlockIngQueue.this.first;
    }
    
    Node<E> nextNode(Node<E> n) {
      return n.next;
    }
  }
  
  private class DescendingItr extends AbstractItr {
    private DescendingItr() {}
    
    Node<E> firstNode() {
      return ResizableCapacityLinkedBlockIngQueue.this.last;
    }
    
    Node<E> nextNode(Node<E> n) {
      return n.prev;
    }
  }
  
  static final class LBDSpliterator<E> implements Spliterator<E> {
    static final int MAX_BATCH = 33554432;
    
    final ResizableCapacityLinkedBlockIngQueue<E> queue;
    
    Node<E> current;
    
    int batch;
    
    boolean exhausted;
    
    long est;
    
    LBDSpliterator(ResizableCapacityLinkedBlockIngQueue<E> queue) {
      this.queue = queue;
      this.est = queue.size();
    }
    
    public long estimateSize() {
      return this.est;
    }
    
    public Spliterator<E> trySplit() {
      ResizableCapacityLinkedBlockIngQueue<E> q = this.queue;
      int b = this.batch;
      int n = (b <= 0) ? 1 : ((b >= 33554432) ? 33554432 : (b + 1));
      Node<E> h;
      if (!this.exhausted && ((h = this.current) != null || (h = q.first) != null) && h.next != null) {
        Object[] a = new Object[n];
        ReentrantLock lock = q.lock;
        int i = 0;
        Node<E> p = this.current;
        lock.lock();
        try {
          if (p != null || (p = q.first) != null)
            do {
              a[i] = p.item;
              if (p.item == null)
                continue; 
              i++;
            } while ((p = p.next) != null && i < n); 
        } finally {
          lock.unlock();
        } 
        if ((this.current = p) == null) {
          this.est = 0L;
          this.exhausted = true;
        } else if ((this.est -= i) < 0L) {
          this.est = 0L;
        } 
        if (i > 0) {
          this.batch = i;
          return 
            Spliterators.spliterator(a, 0, i, 4368);
        } 
      } 
      return null;
    }
    
    public void forEachRemaining(Consumer<? super E> action) {
      if (action == null)
        throw new NullPointerException(); 
      ResizableCapacityLinkedBlockIngQueue<E> q = this.queue;
      ReentrantLock lock = q.lock;
      if (!this.exhausted) {
        this.exhausted = true;
        Node<E> p = this.current;
        do {
          E e = null;
          lock.lock();
          try {
            if (p == null)
              p = q.first; 
            while (p != null) {
              e = p.item;
              p = p.next;
              if (e != null)
                break; 
            } 
          } finally {
            lock.unlock();
          } 
          if (e == null)
            continue; 
          action.accept(e);
        } while (p != null);
      } 
    }
    
    public boolean tryAdvance(Consumer<? super E> action) {
      if (action == null)
        throw new NullPointerException(); 
      ResizableCapacityLinkedBlockIngQueue<E> q = this.queue;
      ReentrantLock lock = q.lock;
      if (!this.exhausted) {
        E e = null;
        lock.lock();
        try {
          if (this.current == null)
            this.current = q.first; 
          while (this.current != null) {
            e = this.current.item;
            this.current = this.current.next;
            if (e != null)
              break; 
          } 
        } finally {
          lock.unlock();
        } 
        if (this.current == null)
          this.exhausted = true; 
        if (e != null) {
          action.accept(e);
          return true;
        } 
      } 
      return false;
    }
    
    public int characteristics() {
      return 4368;
    }
  }
  
  public Spliterator<E> spliterator() {
    return new LBDSpliterator<>(this);
  }
  
  private void writeObject(ObjectOutputStream s) throws IOException {
    ReentrantLock lock = this.lock;
    lock.lock();
    try {
      s.defaultWriteObject();
      for (Node<E> p = this.first; p != null; p = p.next)
        s.writeObject(p.item); 
      s.writeObject(null);
    } finally {
      lock.unlock();
    } 
  }
  
  private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
    s.defaultReadObject();
    this.count = 0;
    this.first = null;
    this.last = null;
    while (true) {
      E item = (E)s.readObject();
      if (item == null)
        break; 
      add(item);
    } 
  }
}
